# -*- coding: utf-8 -*-
"""
   Copyright (C) 2011 Miguel de Dios

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   higher any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
"""

import os
import pygame
from pygame.locals import *
import sqlite3
import subprocess
import random
import string
from pprint import *

from Util import *

from ListFullScreen_mode import *
from ListWeird_mode import *
from ListPanelScreen_mode import *
from ListGrid_mode import *

from ScreenSaver import *

from Config_mode import *



class Frontend:
	framesPerSecond = 60
	clock = None
	list_roms = None
	debug = False
	admin_mode = False
	util = None
	list_modes = ["normal","favorities"]
	current_mode = 0
	default_background = None
	last_snap_found = False
	screensaver = None
	can_shutdown = True
	
	
	def __init__(self, config_mode = False, util_param = None):
		self.util = util_param
		self.clock = pygame.time.Clock()
		self.last_time_screensaver = pygame.time.get_ticks()
		
		# Mode of list the roms (normal or favorities)
		self.load_current_mode()
		
		if config_mode:
			self.config_mode()
		else:
			self.normal_mode()
	
	
	def config_mode(self):
		self.can_shutdown = False
		
		width = self.util.options["screen_width"]
		height = self.util.options["screen_height"]
		
		pygame.init()
		pygame.mouse.set_visible(False)
		pygame.display.set_caption('pyRetro: Config Mode')
		
		self.joy_init()
		
		if not self.util.options['windowed']:
			screen_vars = pygame.FULLSCREEN | pygame.HWSURFACE | pygame.DOUBLEBUF
		else:
			screen_vars = 0
		
		if (width == 0) or (height == 0):
			mode = pygame.display.list_modes()[0] #Set the max resolution avaliable
			
			self.util.options["screen_width"] = mode[0]
			self.util.options["screen_height"] = mode[1]
		else:
			mode = (width, height)
		self.screen = pygame.display.set_mode(mode, screen_vars, 32) # force 32bit mode (for smooth scale)
		
		config = Config_mode(self.util, self)
		config.run()
		
		pygame.quit()
	
	
	def normal_mode(self):
		width = self.util.options["screen_width"]
		height = self.util.options["screen_height"]
		
		if self.util.options['filter'] == 0:
			self.switch_mode(0, False)
		else:
			self.list_roms = self.get_roms_with_buttons(self.util.options['filter_game_buttons'])
		
		if not self.list_roms:
			print("Empty list roms, please check the DB.")
			return
		
		pygame.init()
		pygame.mouse.set_visible(False)
		pygame.display.set_caption('pyRetro: Init')
		#TODO: little hacky but pygame-movie requires it here if you want sound (do not work in ListPanelScreen_mode.py)
		#	maybe we should init just joy/screen on pygame.init and remove this shit :?
		pygame.mixer.quit()
		
		self.joy_init()
		
		if not self.util.options['windowed']:
			screen_vars = pygame.FULLSCREEN | pygame.HWSURFACE | pygame.DOUBLEBUF
		else:
			screen_vars = 0
		
		if (width == 0) or (height == 0):
			mode = pygame.display.list_modes()[0] #Set the max resolution avaliable
			
			self.util.options["screen_width"] = mode[0]
			self.util.options["screen_height"] = mode[1]
		else:
			mode = (width, height)
		self.screen = pygame.display.set_mode(mode, screen_vars, 32) # force 32bit mode (for smooth scale)
		
		if self.util.options["view_mode"] == 1:
			pygame.display.set_caption('pyRetro: Snapshop Fullscreen mode')
			mode = ListFullScreen_mode(self.util, self)
			mode.run()
		elif self.util.options["view_mode"] == 2:
			pygame.display.set_caption('pyRetro: Grid mode')
			mode = ListGrid_mode(self.util, self)
			mode.run()
		elif self.util.options["view_mode"] == 3:
			pygame.display.set_caption('pyRetro: Layout mode')
			mode = ListPanelScreen_mode(self.util, self)
			mode.run()
		elif self.util.options["view_mode"] == 4:
			pygame.display.set_caption('pyRetro: Snapshop Weird mode')
			mode = ListWeird_mode(self.util, self)
			mode.run()
		
		pygame.mixer.init() #TODO: little hacky but pygame-movie requires it here (do not work in ListPanelScreen_mode.py)
		pygame.quit()
	
	
	def execute_rom(self, select_rom):
		#Check if the empty list roms as the empty favorities
		if not self.list_roms[0][0]:
			return
		
		self.screensaver.set_screensaver(False)
		
		#clear screen
		self.screen.fill((0, 0, 0))
		pygame.display.update()
		
		connection = sqlite3.connect(self.util.frontend_db)
		cursor = connection.cursor()
		cursor.execute("UPDATE roms SET times_executed = times_executed + 1 WHERE id = %i;" % self.list_roms[select_rom][4])
		connection.commit()
		
		pygame.joystick.quit() # i dont want get joy events during game
		if not self.util.options['windowed']:
			pygame.display.toggle_fullscreen()
		
		command = self.util.options["mame_executable"] + " " + self.util.options["mame_options"] + " " + self.list_roms[select_rom][2]
		command += " " + self.list_roms[select_rom][0]
		print(command)
		proc = subprocess.Popen(command, shell=True)
		proc.wait()
		
		if not self.util.options['windowed']:
			pygame.display.toggle_fullscreen()
		self.joy_init() # re-init joy again
		self.screensaver.set_screensaver(True)
	
	
	def translate_idrom_to_arraykey(self, idrom):
		return_var = None
		
		iterator = 0
		for rom in self.list_roms:
			if rom[4] == idrom:
				return_var = iterator
				break
			
			iterator += 1
		
		
		return return_var
	
	
	def waitFrame(self):
		self.clock.tick(self.framesPerSecond)
	
	
	def load_snap(self, select_rom, default_snap = None, size = None):
		return_snap = None
		
		self.last_snap_found = True
		
		snap_dir = self.util.options["snapshots_directory"]
		#Try if exists some snapshot_dir and load some select_rom.png files there
		
		if os.path.exists(snap_dir):
			snap_file = os.path.join(snap_dir, self.list_roms[select_rom][0] + ".png")
			
			if os.path.isfile(snap_file):
				return_snap = pygame.image.load(snap_file).convert()
			else:
				#Maybe the mame puts the snapshot as snap_dir/[select_rom]/0000.png
				snap_dir = os.path.join(self.util.options["snapshots_directory"], self.list_roms[select_rom][0])
				
				if os.path.exists(snap_dir):
					snaps = os.listdir(snap_dir)
					if snaps:
						snap_file = snaps.pop()
						snap_file = os.path.join(snap_dir, snap_file)
						if os.path.isfile(snap_file):
							return_snap = pygame.image.load(snap_file).convert()
		
		
		if return_snap:
			if size:
				return_snap = self.resize_with_aspect(return_snap, size[0], size[1])
			else:
				return_snap = self.resize_to_screen(return_snap)
		else:
			self.last_snap_found = False
			if default_snap:
				return_snap = default_snap
			else:
				return_snap = self.default_background
		
		return return_snap
	
	
	def create_default_background(self):
		self.default_background = pygame.Surface((self.util.options["screen_width"], self.util.options["screen_height"]))
		self.default_background.fill((0, 0, 0))
		
		temp = pygame.image.load(self.util.options["default_background"]).convert()
		width = temp.get_width()
		height = temp.get_height()
		
		dif_h = float(self.util.options["screen_height"]) / height
		dif_h = int(math.ceil(dif_h))
		dif_w = float(self.util.options["screen_width"]) / width
		dif_w = int(math.ceil(dif_w))
		
		if dif_h < 1:
			dif_h = 1
		
		if dif_w < 1:
			dif_w = 1
		
		#Fill all background with the mosaic of background file
		for iterator1 in range(dif_h):
			for iterator2 in range(dif_w):
				self.default_background.blit(temp, (iterator2 * width, iterator1 * height))
	
	
	def reset_screen_saver(self):
		if not self.screensaver:
			self.screensaver = ScreenSaver(self.util, self)
		
		self.screensaver.set_screensaver(True)
	
	def screen_saver(self):
		return_var = None
		
		if not self.screensaver:
			self.screensaver = ScreenSaver(self.util, self)
		
		if pygame.event.peek((JOYBUTTONDOWN, JOYAXISMOTION, KEYDOWN)):
			self.screensaver.set_screensaver(False)
		else:
			if self.screensaver.disable_screensaver:
				self.screensaver.set_screensaver(True)
			
			return_var = self.screensaver.start()
		
		return return_var
	
	
	def get_resize_with_aspect(self, org_size, max_size):
		width, height = org_size
		max_width, max_height = max_size
		
		img_scale_x = float(max_width) / float(width)
		img_scale_y = float(max_height) / float(height)
		#scale to width or height - from wah!cade
		if (height) * img_scale_x > max_height:
			#scale to height
			new_width = int(float(width) * img_scale_y)
			new_height = int(float(height) * img_scale_y)
		else:
			#scale to width - from wah!cade
			new_width = int(float(width) * img_scale_x)
			new_height = int(float(height) * img_scale_x)
		
		return new_width, new_height
	
	
	def resize_to_screen(self, image):
		new_width, new_height = self.get_resize_with_aspect(image.get_size(), (self.util.options["screen_width"], self.util.options["screen_height"]))
		return_image = pygame.transform.smoothscale(image, (new_width, new_height))
		return return_image
	
	
	def resize_with_aspect(self, image, max_width, max_height):
		new_width, new_height = self.get_resize_with_aspect(image.get_size(), (max_width, max_height))
		return_image = pygame.transform.smoothscale(image, (new_width, new_height))
		
		return return_image
	
	
	def get_all_roms(self):
		connection = sqlite3.connect(self.util.frontend_db)
		cursor = connection.cursor()
		sql = "SELECT rom, name, custom_options, status, roms.id, year, manufacturer, display_type, "
		sql += "display_screen, input_players, input_control, input_buttons FROM roms LEFT JOIN rom_info "
		sql += "ON roms.id = rom_info.id WHERE disabled = 0 AND found = 1 ORDER BY name ASC;"
		cursor.execute(sql)
		return cursor.fetchall()
	
	
	def add_to_favorities(self, select_rom):
		"""Adds a rom to favorities"""
		token = "favorities"
		rom_id = self.list_roms[select_rom][4] # ID
		s = self.util.load_value_db(token)
		if s:
			l = s.split(',')
			try:
				l.index(str(rom_id))
			except ValueError:
				s += ","+str(rom_id)
		else:
			s = str(rom_id)
		self.util.save_value_db(token,s)
	
	
	def remove_from_favorities(self, select_rom):
		"""Delete a rom from favorities"""
		token = "favorities"
		rom_id = self.list_roms[select_rom][4] # Rom ID
		s = self.util.load_value_db(token)
		if s:
			l = s.split(",")
			del l[l.index(str(rom_id))]
			self.util.save_value_db(token,string.join(l,","))
			self.list_roms = self.get_favorite_roms()
			select_rom = (select_rom - 1) % len(self.list_roms)
		return select_rom
	
	
	def get_favorite_roms(self):
		"""Load the list of favorite roms"""
		token = "favorities"
		s = self.util.load_value_db(token)
		if s:
			connection = sqlite3.connect(self.util.frontend_db)
			cursor = connection.cursor()
			sql = "SELECT rom, name, custom_options, status, roms.id, year, manufacturer, "
			sql += "display_type, display_screen, input_players, input_control, input_buttons "
			sql += "FROM roms LEFT JOIN rom_info ON roms.id = rom_info.id WHERE roms.id IN "
			sql += "("+s+") AND disabled = 0 AND found = 1 ORDER BY name ASC;"
			
			cursor.execute(sql)
			return cursor.fetchall()
		else:
			return [["","EMPTY ROM LIST","","","","","","","","",0,0]]
	
	
	def show_shutdown_message(self):
		if self.show_dialog_countdown_hold_buttons("Hold 5 seconds to shudown he arcade machine", 5, [K_1, K_RETURN]):
			if self.util.options['shutdown_on_exit']:
				os.system(self.util.options['shutdown_command'])
	
	
	def toggle_fav(self, select_rom):
		"""Add/remove from favorities"""
		if self.util.options['user_can_edit_favorities'] == 0:
			#Do none
			return select_rom
		
		if self.list_modes[self.current_mode] == "favorities":
			if self.show_dialog("Are you sure to remove from favorities?"):
				select_rom = self.remove_from_favorities(select_rom)
		elif self.list_modes[self.current_mode] == "normal":
			if self.show_dialog("Do you want to add to favorities?"):
				self.add_to_favorities(select_rom)
		return select_rom
	
	
	def switch_mode(self, select_rom, toggle = True):
		"""Toggle list mode between favorities and normal
		return modified select_rom"""
		
		if toggle:
			self.current_mode = (self.current_mode + 1) % len(self.list_modes)
		
		if self.list_modes[self.current_mode] == "favorities":
			self.list_roms = self.get_favorite_roms()
			if len(self.list_roms) > 0:
				select_rom %= len(self.list_roms)
		elif self.list_modes[self.current_mode] == "normal":
			if self.util.options['filter'] == 0:
				self.list_roms = self.get_all_roms()
			else:
				self.list_roms = self.get_roms_with_buttons(self.util.options['filter_game_buttons'])
			
			if len(self.list_roms) > 0:
				select_rom %= len(self.list_roms)
			
		return select_rom
	
	
	def load_current_mode(self):
		token = "current_mode"
		m = self.util.load_value_db(token)
		if m:
			self.current_mode = int(m) # m=0 normal, m=1 favorities
		if self.list_modes[self.current_mode] == "favorities":
			self.list_roms = self.get_favorite_roms()
	
	
	def save_current_mode(self):
		token = "current_mode"
		self.util.save_value_db(token, self.current_mode)
	
	
	def get_roms_with_buttons(self, buttons):
		"""Load the list of roms having n. of buttons < buttons param"""
		connection = sqlite3.connect(self.util.frontend_db)
		cursor = connection.cursor()
		sql = "SELECT rom, name, custom_options, status, roms.id, year, manufacturer, display_type, "
		sql += "display_screen, input_players, input_control, input_buttons FROM roms LEFT JOIN rom_info "
		sql += "ON roms.id = rom_info.id WHERE disabled = 0 AND found = 1 AND (input_buttons between 0 and "
		sql += str(buttons)+ " OR input_buttons = -1) ORDER BY name ASC;"
		cursor.execute(sql)
		return cursor.fetchall()
	
	
	def show_dialog_countdown_hold_buttons(self, message, seconds_wait, buttons):
		fontname = "Arial"
		
		WIDTH = 400
		HEIGHT = 200
		# TODO: optimize a lot the whole thing
		# Prepare the dialog surface
		color = (255, 255, 0)
		surface = pygame.Surface((WIDTH, HEIGHT))
		
		myFont = pygame.font.SysFont(fontname, 15)
		message_surface =  myFont.render(message, True, color)
		message_surface.set_alpha(128)
		message_position = ((WIDTH - message_surface.get_width()) / 2,
			(HEIGHT - message_surface.get_height()) /2 - 15)
		surface.blit(message_surface, message_position)
		
		myFont_countdown = pygame.font.SysFont(fontname, 20)
		countdown_surface =  myFont_countdown.render(str(seconds_wait), True, color)
		countdown_position = ((WIDTH - countdown_surface.get_width()) / 2,
			(HEIGHT + (HEIGHT / 2) - countdown_surface.get_height()) /2 - 20)
		surface.blit(countdown_surface, countdown_position)
		
		
		pos_dialog_center = ((self.screen.get_width()-WIDTH) / 2, (self.screen.get_height() - HEIGHT) / 2)
		
		return_var = False
		exit_var = False
		start_to_show = pygame.time.get_ticks()
		while not exit_var:
			now = pygame.time.get_ticks()
			
			if (now - start_to_show) >= 1000:
				seconds_wait -= 1
				start_to_show = now
			
			if seconds_wait == 0:
				return_var = True
				exit_var = True
			
			# Event dispatcher
			ekey = self.get_events()
			if ekey < 0:
				ekey = -ekey
				
				if ekey in buttons:
					return_var = False
					exit_var = True
			
			# draw the dialog
			pygame.draw.rect(surface, (255,255,255), pygame.Rect(0,0,WIDTH,HEIGHT), 1)
			eraser_surface = pygame.Surface((countdown_surface.get_width(), countdown_surface.get_height()))
			surface.blit(eraser_surface, countdown_position)
			countdown_surface =  myFont_countdown.render(str(seconds_wait), True, color)
			countdown_position = ((WIDTH - countdown_surface.get_width()) / 2,
				(HEIGHT + (HEIGHT / 2) - countdown_surface.get_height()) /2 - 20)
			surface.blit(countdown_surface, countdown_position)
			self.screen.blit(surface, pos_dialog_center)
			pygame.display.update()
		
		return return_var
	
	
	def show_dialog(self, question):
		"""
		Display a question dialog
		returns True = Yes and False = No
		"""
		fontname = "Arial"
		
		WIDTH = 400
		HEIGHT = 200
		# TODO: optimize a lot the whole thing
		# Prepare the dialog surface
		color = (255, 255, 0)
		surface = pygame.Surface((WIDTH, HEIGHT))
		myFont = pygame.font.SysFont(fontname, 15)
		
		message_surface =  myFont.render(question, True, color)
		message_surface.set_alpha(128)
		yes_surface = myFont.render("Yes", True, color)
		no_surface = myFont.render("No", True, color)
		selector_surfaces = [myFont.render("<<", True, color),myFont.render(">>", True, color)]
		selector_erasers = [pygame.Surface((selector_surfaces[0].get_width(), selector_surfaces[0].get_height())),
							pygame.Surface((selector_surfaces[1].get_width(), selector_surfaces[1].get_height()))]
		
		message_position = ((WIDTH - message_surface.get_width()) / 2,
			(HEIGHT - message_surface.get_height()) /2 - 15)
		yes_position = (message_position[0],HEIGHT - yes_surface.get_height() - 50)
		no_position = (message_position[0] + message_surface.get_width() - no_surface.get_width(), HEIGHT - no_surface.get_height() - 50)
		selector_positions = [(yes_position[0]+yes_surface.get_width(),yes_position[1]),
			(no_position[0] - no_surface.get_width(),no_position[1])]
		selector_position = selector_positions[0]
		
		surface.blit(message_surface,message_position)
		surface.blit(yes_surface, yes_position)
		surface.blit(no_surface, no_position)
		pos, pos_rev = 1, 0 # default in "no" selected
		exit_var = False
		pos_dialog_center = ((self.screen.get_width() - WIDTH) / 2, (self.screen.get_height() - HEIGHT) / 2)
		
		return_var = None
		
		while not exit_var:
			now = pygame.time.get_ticks()
			# Event dispatcher
			ekey = self.get_events()
			
			#Invert for to check the keyup
			ekey = -ekey
			
			if ekey == K_LEFT:
				pos, pos_rev = 0, 1
			if ekey == K_RIGHT:
				pos, pos_rev = 1, 0
			if ekey == K_RETURN or ekey == K_LCTRL:
				return_var = bool(pos_rev)
				exit_var = True
			
			# draw the dialog
			pygame.draw.rect(surface, (255,255,255), pygame.Rect(0,0,WIDTH,HEIGHT), 1)
			surface.blit(selector_erasers[pos_rev], selector_positions[pos_rev])
			surface.blit(selector_surfaces[pos], selector_positions[pos])
			self.screen.blit(surface, pos_dialog_center)
			
			self.waitFrame()
			pygame.display.update()
		
		return return_var
	
	def joy_init(self):
		if pygame.joystick.get_init():
			pygame.joystick.quit()
		pygame.joystick.init()
		self.joys = pygame.joystick.get_count()
		if self.joys > 0:
			stick = pygame.joystick.Joystick(0)
			stick.init() # now we will receive events for the joystick
		return True
	
	def get_events(self):
		OK = [pygame.JOYBUTTONDOWN, pygame.JOYBUTTONUP, pygame.JOYAXISMOTION, pygame.KEYDOWN, pygame.KEYUP, pygame.QUIT]
		done = False
		res = 0
		while not done:
			pygame.event.pump()
			ev_list = pygame.event.get(OK)
			for ev in ev_list:
				done = True
				if ev.type == QUIT:
					res = K_ESCAPE
				if ev.type == pygame.JOYBUTTONDOWN:
					if ev.button == 0:
						res = K_RETURN
					elif ev.button == 2:
						res = K_SPACE
				elif ev.type == pygame.JOYAXISMOTION:
					if ev.axis: # axis Y
						if ev.value > 0: # down
							res = K_DOWN
						elif ev.value < 0: # up
							res = K_UP
						else: # return to center
							res = -1 # joy up
					else:
						if ev.value > 0: #der
							res = K_RIGHT
						elif ev.value < 0: #izq
							res = K_LEFT
						else: # return to center
							res = -1 # joy up
				elif ev.type == pygame.KEYDOWN:
						res = ev.key
				elif ev.type == pygame.KEYUP:
					res = ev.key * (-1)
			
			return res

